Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tty: n_gsm: add ioctl for DLC specific parameter configuration

Parameter negotiation has been introduced with
commit 92f1f0c3290d ("tty: n_gsm: add parameter negotiation support")

However, means to set individual parameters per DLCI are not yet
implemented. Furthermore, it is currently not possible to keep a DLCI half
open until the user application sets the right parameters for it. This is
required to allow a user application to set its specific parameters before
the underlying link is established. Otherwise, the link is opened and
re-established right afterwards if the user application sets incompatible
parameters. This may be an unexpected behavior for the peer.

Add parameter 'wait_config' to 'gsm_config' to support setups where the
DLCI specific user application sets its specific parameters after open()
and before the link gets fully established. Setting this to zero disables
the user application specific DLCI configuration option.

Add the ioctls 'GSMIOC_GETCONF_DLCI' and 'GSMIOC_SETCONF_DLCI' for the
ldisc and virtual ttys. This gets/sets the DLCI specific parameters and may
trigger a reconnect of the DLCI if incompatible values have been set. Only
the parameters for the DLCI associated with the virtual tty can be set or
retrieved if called on these.

Add remark within the documentation to introduce the new ioctls.

Link: https://lore.kernel.org/oe-kbuild-all/202302281856.S9Lz4gHB-lkp@intel.com/
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
Link: https://lore.kernel.org/r/20230315105354.6234-1-daniel.starke@siemens.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Daniel Starke and committed by
Greg Kroah-Hartman
4ca58966 cb95de8d

+207 -6
+4
Documentation/driver-api/tty/n_gsm.rst
··· 29 29 30 30 #. Configure the mux using ``GSMIOC_GETCONF``/``GSMIOC_SETCONF`` ioctl. 31 31 32 + #. Configure DLCs using ``GSMIOC_GETCONF_DLCI``/``GSMIOC_SETCONF_DLCI`` ioctl for non-defaults. 33 + 32 34 #. Obtain base gsmtty number for the used serial port. 33 35 34 36 Major parts of the initialization program ··· 121 119 ioctl if needed. 122 120 123 121 #. Configure the mux using ``GSMIOC_GETCONF``/``GSMIOC_SETCONF`` ioctl. 122 + 123 + #. Configure DLCs using ``GSMIOC_GETCONF_DLCI``/``GSMIOC_SETCONF_DLCI`` ioctl for non-defaults. 124 124 125 125 #. Obtain base gsmtty number for the used serial port:: 126 126
+187 -5
drivers/tty/n_gsm.c
··· 128 128 129 129 enum gsm_dlci_state { 130 130 DLCI_CLOSED, 131 + DLCI_WAITING_CONFIG, /* Waiting for DLCI configuration from user */ 131 132 DLCI_CONFIGURE, /* Sending PN (for adaption > 1) */ 132 133 DLCI_OPENING, /* Sending SABM not seen UA */ 133 134 DLCI_OPEN, /* SABM/UA complete */ ··· 331 330 unsigned int t3; /* Power wake-up timer in seconds. */ 332 331 int n2; /* Retry count */ 333 332 u8 k; /* Window size */ 333 + bool wait_config; /* Wait for configuration by ioctl before DLCI open */ 334 334 u32 keep_alive; /* Control channel keep-alive in 10ms */ 335 335 336 336 /* Statistics (not currently exposed) */ ··· 453 451 static struct gsm_msg *gsm_data_alloc(struct gsm_mux *gsm, u8 addr, int len, 454 452 u8 ctrl); 455 453 static int gsm_send_packet(struct gsm_mux *gsm, struct gsm_msg *msg); 454 + static struct gsm_dlci *gsm_dlci_alloc(struct gsm_mux *gsm, int addr); 456 455 static void gsmld_write_trigger(struct gsm_mux *gsm); 457 456 static void gsmld_write_task(struct work_struct *work); 458 457 ··· 2283 2280 2284 2281 switch (dlci->state) { 2285 2282 case DLCI_CLOSED: 2283 + case DLCI_WAITING_CONFIG: 2286 2284 case DLCI_CLOSING: 2287 2285 dlci->retries = gsm->n2; 2288 2286 if (!need_pn) { ··· 2315 2311 { 2316 2312 switch (dlci->state) { 2317 2313 case DLCI_CLOSED: 2314 + case DLCI_WAITING_CONFIG: 2318 2315 case DLCI_CLOSING: 2319 2316 dlci->state = DLCI_OPENING; 2317 + break; 2318 + default: 2319 + break; 2320 + } 2321 + } 2322 + 2323 + /** 2324 + * gsm_dlci_set_wait_config - wait for channel configuration 2325 + * @dlci: DLCI to configure 2326 + * 2327 + * Wait for a DLCI configuration from the application. 2328 + */ 2329 + static void gsm_dlci_set_wait_config(struct gsm_dlci *dlci) 2330 + { 2331 + switch (dlci->state) { 2332 + case DLCI_CLOSED: 2333 + case DLCI_CLOSING: 2334 + dlci->state = DLCI_WAITING_CONFIG; 2320 2335 break; 2321 2336 default: 2322 2337 break; ··· 2474 2451 2475 2452 if (sent && debug & DBG_DATA) 2476 2453 pr_info("%s TX queue stalled\n", __func__); 2454 + } 2455 + 2456 + /** 2457 + * gsm_dlci_copy_config_values - copy DLCI configuration 2458 + * @dlci: source DLCI 2459 + * @dc: configuration structure to fill 2460 + */ 2461 + static void gsm_dlci_copy_config_values(struct gsm_dlci *dlci, struct gsm_dlci_config *dc) 2462 + { 2463 + memset(dc, 0, sizeof(*dc)); 2464 + dc->channel = (u32)dlci->addr; 2465 + dc->adaption = (u32)dlci->adaption; 2466 + dc->mtu = (u32)dlci->mtu; 2467 + dc->priority = (u32)dlci->prio; 2468 + if (dlci->ftype == UIH) 2469 + dc->i = 1; 2470 + else 2471 + dc->i = 2; 2472 + dc->k = (u32)dlci->k; 2473 + } 2474 + 2475 + /** 2476 + * gsm_dlci_config - configure DLCI from configuration 2477 + * @dlci: DLCI to configure 2478 + * @dc: DLCI configuration 2479 + * @open: open DLCI after configuration? 2480 + */ 2481 + static int gsm_dlci_config(struct gsm_dlci *dlci, struct gsm_dlci_config *dc, int open) 2482 + { 2483 + struct gsm_mux *gsm; 2484 + bool need_restart = false; 2485 + bool need_open = false; 2486 + unsigned int i; 2487 + 2488 + /* 2489 + * Check that userspace doesn't put stuff in here to prevent breakages 2490 + * in the future. 2491 + */ 2492 + for (i = 0; i < ARRAY_SIZE(dc->reserved); i++) 2493 + if (dc->reserved[i]) 2494 + return -EINVAL; 2495 + 2496 + if (!dlci) 2497 + return -EINVAL; 2498 + gsm = dlci->gsm; 2499 + 2500 + /* Stuff we don't support yet - I frame transport */ 2501 + if (dc->adaption != 1 && dc->adaption != 2) 2502 + return -EOPNOTSUPP; 2503 + if (dc->mtu > MAX_MTU || dc->mtu < MIN_MTU || dc->mtu > gsm->mru) 2504 + return -EINVAL; 2505 + if (dc->priority >= 64) 2506 + return -EINVAL; 2507 + if (dc->i == 0 || dc->i > 2) /* UIH and UI only */ 2508 + return -EINVAL; 2509 + if (dc->k > 7) 2510 + return -EINVAL; 2511 + 2512 + /* 2513 + * See what is needed for reconfiguration 2514 + */ 2515 + /* Framing fields */ 2516 + if (dc->adaption != dlci->adaption) 2517 + need_restart = true; 2518 + if (dc->mtu != dlci->mtu) 2519 + need_restart = true; 2520 + if (dc->i != dlci->ftype) 2521 + need_restart = true; 2522 + /* Requires care */ 2523 + if (dc->priority != dlci->prio) 2524 + need_restart = true; 2525 + 2526 + if ((open && gsm->wait_config) || need_restart) 2527 + need_open = true; 2528 + if (dlci->state == DLCI_WAITING_CONFIG) { 2529 + need_restart = false; 2530 + need_open = true; 2531 + } 2532 + 2533 + /* 2534 + * Close down what is needed, restart and initiate the new 2535 + * configuration. 2536 + */ 2537 + if (need_restart) { 2538 + gsm_dlci_begin_close(dlci); 2539 + wait_event_interruptible(gsm->event, dlci->state == DLCI_CLOSED); 2540 + if (signal_pending(current)) 2541 + return -EINTR; 2542 + } 2543 + /* 2544 + * Setup the new configuration values 2545 + */ 2546 + dlci->adaption = (int)dc->adaption; 2547 + 2548 + if (dc->mtu) 2549 + dlci->mtu = (unsigned int)dc->mtu; 2550 + else 2551 + dlci->mtu = gsm->mtu; 2552 + 2553 + if (dc->priority) 2554 + dlci->prio = (u8)dc->priority; 2555 + else 2556 + dlci->prio = roundup(dlci->addr + 1, 8) - 1; 2557 + 2558 + if (dc->i == 1) 2559 + dlci->ftype = UIH; 2560 + else if (dc->i == 2) 2561 + dlci->ftype = UI; 2562 + 2563 + if (dc->k) 2564 + dlci->k = (u8)dc->k; 2565 + else 2566 + dlci->k = gsm->k; 2567 + 2568 + if (need_open) { 2569 + if (gsm->initiator) 2570 + gsm_dlci_begin_open(dlci); 2571 + else 2572 + gsm_dlci_set_opening(dlci); 2573 + } 2574 + 2575 + return 0; 2477 2576 } 2478 2577 2479 2578 /* ··· 3223 3078 gsm->mru = 64; /* Default to encoding 1 so these should be 64 */ 3224 3079 gsm->mtu = 64; 3225 3080 gsm->dead = true; /* Avoid early tty opens */ 3081 + gsm->wait_config = false; /* Disabled */ 3226 3082 gsm->keep_alive = 0; /* Disabled */ 3227 3083 3228 3084 /* Store the instance to the mux array or abort if no space is ··· 3364 3218 struct gsm_config_ext *ce) 3365 3219 { 3366 3220 memset(ce, 0, sizeof(*ce)); 3221 + ce->wait_config = gsm->wait_config ? 1 : 0; 3367 3222 ce->keep_alive = gsm->keep_alive; 3368 3223 } 3369 3224 ··· 3380 3233 if (ce->reserved[i]) 3381 3234 return -EINVAL; 3382 3235 3236 + /* 3237 + * Setup the new configuration values 3238 + */ 3239 + gsm->wait_config = ce->wait_config ? true : false; 3383 3240 gsm->keep_alive = ce->keep_alive; 3241 + 3384 3242 return 0; 3385 3243 } 3386 3244 ··· 3587 3435 /* Attach the initial passive connection */ 3588 3436 gsm->encoding = GSM_ADV_OPT; 3589 3437 gsmld_attach_gsm(tty, gsm); 3438 + 3439 + /* The mux will not be activated yet, we wait for correct 3440 + * configuration first. 3441 + */ 3442 + if (gsm->encoding == GSM_BASIC_OPT) 3443 + gsm->receive = gsm0_receive; 3444 + else 3445 + gsm->receive = gsm1_receive; 3590 3446 3591 3447 return 0; 3592 3448 } ··· 4168 4008 { 4169 4009 struct gsm_dlci *dlci = tty->driver_data; 4170 4010 struct tty_port *port = &dlci->port; 4171 - struct gsm_mux *gsm = dlci->gsm; 4172 4011 4173 4012 port->count++; 4174 4013 tty_port_tty_set(port, tty); ··· 4177 4018 a DM straight back. This is ok as that will have caused a hangup */ 4178 4019 tty_port_set_initialized(port, true); 4179 4020 /* Start sending off SABM messages */ 4180 - if (gsm->initiator) 4181 - gsm_dlci_begin_open(dlci); 4182 - else 4183 - gsm_dlci_set_opening(dlci); 4021 + if (!dlci->gsm->wait_config) { 4022 + /* Start sending off SABM messages */ 4023 + if (dlci->gsm->initiator) 4024 + gsm_dlci_begin_open(dlci); 4025 + else 4026 + gsm_dlci_set_opening(dlci); 4027 + } else { 4028 + gsm_dlci_set_wait_config(dlci); 4029 + } 4184 4030 /* And wait for virtual carrier */ 4185 4031 return tty_port_block_til_ready(port, tty, filp); 4186 4032 } ··· 4306 4142 { 4307 4143 struct gsm_dlci *dlci = tty->driver_data; 4308 4144 struct gsm_netconfig nc; 4145 + struct gsm_dlci_config dc; 4309 4146 int index; 4310 4147 4311 4148 if (dlci->state == DLCI_CLOSED) ··· 4330 4165 gsm_destroy_network(dlci); 4331 4166 mutex_unlock(&dlci->mutex); 4332 4167 return 0; 4168 + case GSMIOC_GETCONF_DLCI: 4169 + if (copy_from_user(&dc, (void __user *)arg, sizeof(dc))) 4170 + return -EFAULT; 4171 + if (dc.channel != dlci->addr) 4172 + return -EPERM; 4173 + gsm_dlci_copy_config_values(dlci, &dc); 4174 + if (copy_to_user((void __user *)arg, &dc, sizeof(dc))) 4175 + return -EFAULT; 4176 + return 0; 4177 + case GSMIOC_SETCONF_DLCI: 4178 + if (copy_from_user(&dc, (void __user *)arg, sizeof(dc))) 4179 + return -EFAULT; 4180 + if (dc.channel >= NUM_DLCI) 4181 + return -EINVAL; 4182 + if (dc.channel != 0 && dc.channel != dlci->addr) 4183 + return -EPERM; 4184 + return gsm_dlci_config(dlci, &dc, 1); 4333 4185 case TIOCMIWAIT: 4334 4186 return gsm_wait_modem_change(dlci, (u32)arg); 4335 4187 default:
+16 -1
include/uapi/linux/gsmmux.h
··· 43 43 __u32 keep_alive; /* Control channel keep-alive in 1/100th of a 44 44 * second (0 to disable) 45 45 */ 46 - __u32 reserved[7]; /* For future use, must be initialized to zero */ 46 + __u32 wait_config; /* Wait for DLCI config before opening virtual link? */ 47 + __u32 reserved[6]; /* For future use, must be initialized to zero */ 47 48 }; 48 49 49 50 #define GSMIOC_GETCONF_EXT _IOR('G', 5, struct gsm_config_ext) 50 51 #define GSMIOC_SETCONF_EXT _IOW('G', 6, struct gsm_config_ext) 52 + 53 + /* Set channel accordingly before calling GSMIOC_GETCONF_DLCI. */ 54 + struct gsm_dlci_config { 55 + __u32 channel; /* DLCI (0 for the associated DLCI) */ 56 + __u32 adaption; /* Convergence layer type */ 57 + __u32 mtu; /* Maximum transfer unit */ 58 + __u32 priority; /* Priority (0 for default value) */ 59 + __u32 i; /* Frame type (1 = UIH, 2 = UI) */ 60 + __u32 k; /* Window size (0 for default value) */ 61 + __u32 reserved[8]; /* For future use, must be initialized to zero */ 62 + }; 63 + 64 + #define GSMIOC_GETCONF_DLCI _IOWR('G', 7, struct gsm_dlci_config) 65 + #define GSMIOC_SETCONF_DLCI _IOW('G', 8, struct gsm_dlci_config) 51 66 52 67 #endif